package com.vito.act.tool;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.impl.util.json.JSONObject;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vito.base.util.Contants;
import com.vito.base.util.SpringApplicationContext;
import com.vito.base.util.StringUtil;

public class ActivitiTool {
	private static Logger log = LoggerFactory.getLogger(ActivitiTool.class);
	private static ProcessEngine processEngine = null;
	private static Map<String,String> processLockMap = new HashMap<String,String>();

	public static ProcessEngine getProcessEngine() {
		if (processEngine == null) {
			try {
				processEngine = SpringApplicationContext.getApplicationContextBean(ProcessEngineFactoryBean.class)
						.getObject();
			} catch (Exception e) {
				log.error("获取工作流ProcessEngine失败", e);
			}
		}
		return processEngine;
	}

	public static List<Task> getTasksByKey(String key) {
		return getProcessEngine().getTaskService().createTaskQuery().taskDefinitionKey(key).list();
	}

	public static RepositoryService getRepositoryService() {
		return getProcessEngine().getRepositoryService();
	}

	public static IdentityService getIdentityService() {
		return getProcessEngine().getIdentityService();
	}

	public static RepositoryServiceImpl getRepositoryServiceImpl() {
		return (RepositoryServiceImpl) getProcessEngine().getRepositoryService();
	}

	public static TaskService getTaskService() {
		return getProcessEngine().getTaskService();
	}

	public static HistoryService getHistoryService() {
		return getProcessEngine().getHistoryService();
	}

	public static RuntimeService getRuntimeService() {
		return getProcessEngine().getRuntimeService();
	}

	public static boolean rollBackWorkFlow(String taskId) {
		RuntimeService runtimeService = getRuntimeService();//
		TaskService taskService = getTaskService();
		RepositoryService repositoryService = getRepositoryService();
		HistoryService historyService = getHistoryService();
		try {
			Map<String, Object> variables;
			// 取得当前任务.当前任务节点
			HistoricTaskInstance currTask = historyService.createHistoricTaskInstanceQuery().taskId(taskId)
					.singleResult();
			// 取得流程实例，流程实例
			ProcessInstance instance = runtimeService.createProcessInstanceQuery()
					.processInstanceId(currTask.getProcessInstanceId()).singleResult();
			if (instance == null) {
				log.debug("流程结束");
				return false;
			}
			variables = instance.getProcessVariables();
			// 取得流程定义
			ProcessDefinitionEntity definition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
					.getDeployedProcessDefinition(currTask.getProcessDefinitionId());
			if (definition == null) {
				log.debug("流程定义未找到");
				return false;
			}
			// 取得上一步活动
			ActivityImpl currActivity = ((ProcessDefinitionImpl) definition)
					.findActivity(currTask.getTaskDefinitionKey());

			// 也就是节点间的连线
			List<PvmTransition> nextTransitionList = currActivity.getIncomingTransitions();
			// 清除当前活动的出口
			List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
			// 新建一个节点连线关系集合

			List<PvmTransition> pvmTransitionList = currActivity.getOutgoingTransitions();
			for (PvmTransition pvmTransition : pvmTransitionList) {
				oriPvmTransitionList.add(pvmTransition);
			}
			pvmTransitionList.clear();
			// 建立新出口
			List<TransitionImpl> newTransitions = new ArrayList<TransitionImpl>();
			for (PvmTransition nextTransition : nextTransitionList) {
				PvmActivity nextActivity = nextTransition.getSource();
				ActivityImpl nextActivityImpl = ((ProcessDefinitionImpl) definition).findActivity(nextActivity.getId());
				TransitionImpl newTransition = currActivity.createOutgoingTransition();
				newTransition.setDestination(nextActivityImpl);
				newTransitions.add(newTransition);
			}
			// 完成任务
			List<Task> tasks = taskService.createTaskQuery().processInstanceId(instance.getId())
					.taskDefinitionKey(currTask.getTaskDefinitionKey()).list();
			for (Task task : tasks) {
				taskService.complete(task.getId(), variables);
				historyService.deleteHistoricTaskInstance(task.getId());
			}
			// 恢复方向
			for (TransitionImpl transitionImpl : newTransitions) {
				currActivity.getOutgoingTransitions().remove(transitionImpl);
			}
			for (PvmTransition pvmTransition : oriPvmTransitionList) {
				pvmTransitionList.add(pvmTransition);
			}
			log.debug("流程结束");
			return true;
		} catch (Exception e) {
			log.error("回退任务失败(" + taskId + ")", e);
		}
		return false;
	}

	/**
	 * 获取当前任务下的连接线的名称 用作按钮
	 * 
	 * @param task
	 */
	public static List<Map<String, Object>> getButtonsForTransition(String processKey, String nodeKey,
			String processInstanceId) {
		if (Contants.FORM_PROCESS_START.equals(nodeKey)) {
			List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
			Map<String, Object> map = new HashMap<String, Object>();
			boolean isAssgin = false;
			String documentation = getRepositoryServiceImpl().createProcessDefinitionQuery()
					.processDefinitionKey(processKey).latestVersion().singleResult().getDescription(); // 取出js
			if (documentation != null && !"".equals(documentation)) {
				JSONObject json = null;
				if (documentation != null) {
					documentation = StringUtil.replaceLineBreak(documentation);
					json = new JSONObject(documentation);
				}
				map.put(ActivitiContants.ACTIVITI_DOCUMENTATION, documentation.toString());
				if (json != null && json.has(ActivitiContants.ACTIVITI_TYPE) && json.get(ActivitiContants.ACTIVITI_TYPE)
						.equals(ActivitiContants.ACTIVITI_PROCESS_TYPE_ASSIGN)) {
					isAssgin = true;
					map.put("id", "assignProcess");
					map.put("name", "提交");
				} else if (json != null && json.has(ActivitiContants.ACTIVITI_TYPE)
						&& json.get(ActivitiContants.ACTIVITI_TYPE)
								.equals(ActivitiContants.ACTIVITI_PROCESS_TYPE_ASSIGNIFISROLE)) {
					isAssgin = true;
					map.put("id", "assignRoleProcess");
					map.put("name", "提交");
				}
			}
			if (!isAssgin) {
				map.put("name", "提交");
			}
			list.add(map);
			return list;
		}
		RepositoryServiceImpl repositoryService = getRepositoryServiceImpl();
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
				.processDefinitionKey(processKey).latestVersion().singleResult();
		ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService
				.getDeployedProcessDefinition(processDefinition.getId());
		List<ActivityImpl> list = definition.getActivities();
		ActivityImpl activityImpl = null;
		for (ActivityImpl p : list) {
			TaskDefinition def = (TaskDefinition) p.getProperty(ActivitiContants.ACTIVITI_PROCESS_TYPE_TASKDEFINITION);
			if (def != null) {
				if (nodeKey == null) {
					activityImpl = p;
					break;
				} else {
					if (nodeKey.equals(def.getKey())) {
						activityImpl = p;
						break;
					}
				}
			}
		}
		if (activityImpl == null) {
			return null;
		}
		List<Map<String, Object>> buttons = new ArrayList<Map<String, Object>>();
		// 判断是否需要出现指派按钮
		String documentation = (String) activityImpl.getProperty(ActivitiContants.ACTIVITI_DOCUMENTATION);
		JSONObject json = null;
		if (documentation != null) {
			documentation = StringUtil.replaceLineBreak(documentation);
			json = new JSONObject(documentation);
		}
		if (json != null && json.has(ActivitiContants.ACTIVITI_TYPE)
				&& json.get(ActivitiContants.ACTIVITI_TYPE).equals(ActivitiContants.ACTIVITI_PROCESS_TYPE_ASSIGN)) {
			Map<String, Object> map = new HashMap<String, Object>();
			map = new HashMap<String, Object>();
			map.put("id", "assignProcess");
			map.put("name", "指派");
			map.put(ActivitiContants.ACTIVITI_DOCUMENTATION, documentation.toString());
			buttons.add(map);
		} else if (json != null && json.has(ActivitiContants.ACTIVITI_TYPE) && json.get(ActivitiContants.ACTIVITI_TYPE)
				.equals(ActivitiContants.ACTIVITI_PROCESS_TYPE_ASSIGNIFISROLE)) {
			Map<String, Object> map = new HashMap<String, Object>();
			map = new HashMap<String, Object>();
			map.put("id", "assignRoleProcess");
			map.put("name", "指派");
			map.put(ActivitiContants.ACTIVITI_DOCUMENTATION, documentation.toString());
			buttons.add(map);
		} else {
			List<PvmTransition> transitions = activityImpl.getOutgoingTransitions();
			for (PvmTransition pvmTransition : transitions) {
				PvmActivity pvmact = pvmTransition.getDestination();
				String type = (String) pvmact.getProperty(ActivitiContants.ACTIVITI_TYPE);
				if (type.equals(ActivitiContants.ACTIVITI_PROCESS_TYPE_EXCLUSIVEGATEWAY)) {// 判断流程是否为互斥网关
																							// 互斥网关获取子按钮
					List<PvmTransition> temp = pvmTransition.getDestination().getOutgoingTransitions();
					for (PvmTransition pvm : temp) {
						Map<String, Object> map = new HashMap<String, Object>();
						String documen = (String) pvm.getProperty(ActivitiContants.ACTIVITI_DOCUMENTATION);
						map.put(ActivitiContants.ACTIVITI_DOCUMENTATION, StringUtil.replaceLineBreak(documen));// 取出js
						String name = (String) pvm.getProperty("name");
						if (name == null) {
							name = "提交";
						}
						map.put("name", name);
						buttons.add(map);
					}
				} else {
					Map<String, Object> map = new HashMap<String, Object>();
					String documen = (String) pvmTransition.getProperty(ActivitiContants.ACTIVITI_DOCUMENTATION);
					map.put(ActivitiContants.ACTIVITI_DOCUMENTATION, StringUtil.replaceLineBreak(documen));// 取出js
					String name = (String) pvmTransition.getProperty("name");
					if (name == null) {
						name = "提交";
					}
					map.put("name", name);
					buttons.add(map);
				}
			}
		}
		ProcessInstance p = getRuntimeService().createProcessInstanceQuery().processInstanceId(processInstanceId)
				.suspended().singleResult();
		if (p == null) {
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("id", "hangUp");
			map.put("name", "挂起");
			buttons.add(map);
		} else {
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("id", "unHangUp");
			map.put("name", "解挂");
			buttons.add(map);
		}
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("id", "endProcess");
		map.put("name", "终止");
		buttons.add(map);

		return buttons;
	}

	/**
	 * @param processInstanceId
	 *            流程实例id
	 * @param isHangUp
	 *            true挂起，false解挂
	 */
	public static void hangUp(String processInstanceId, boolean isHangUp) {
		if (isHangUp) {
			getRuntimeService().suspendProcessInstanceById(processInstanceId);
		} else {
			getRuntimeService().activateProcessInstanceById(processInstanceId);
		}
	}

	/**
	 * @param key
	 *            流程key
	 * @return返回该key下所有节点
	 */
	public static List<TaskDefinition> getAllTaskNode(String key) {
		RepositoryServiceImpl repositoryService = getRepositoryServiceImpl();
		// ProcessDefinition processDefinition
		// =repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).latestVersion().singleResult();
		ProcessDefinition processDefinition = OaActivitiTool.getLastVersionProcess(key);
		ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService
				.getDeployedProcessDefinition(processDefinition.getId());
		List<ActivityImpl> list = definition.getActivities();
		List<TaskDefinition> res = new ArrayList<TaskDefinition>();
		for (ActivityImpl p : list) {
			TaskDefinition def = (TaskDefinition) p.getProperty("taskDefinition");
			if (def != null)
				res.add(def);
		}
		return res;
	}

	/**
	 * @param key
	 *            流程key
	 * @return返回该key下所有节点
	 */
	public static TaskDefinition getTaskNode(String key, String nodeId) {
		if (nodeId == null || "".equals(nodeId)) {
			return null;
		}
		List<TaskDefinition> res = getAllTaskNode(key);
		if (res == null || res.size() == 0) {
			return null;
		}
		for (TaskDefinition task : res) {
			if (nodeId.equals(task.getKey())) {
				return task;
			}
		}
		return null;
	}

	/**
	 * @return 获取所有定义流程
	 */
	public static List<Map<String, Object>> getLastProcessDefinition() {
		RepositoryServiceImpl repositoryService = getRepositoryServiceImpl();
		List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().latestVersion()
				.list();
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		for (ProcessDefinition p : processDefinitions) {
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("key", p.getKey());
			map.put("name", p.getName());
			list.add(map);
		}
		return list;
	}

	/**
	 * @param processInstanceId
	 * @return 根据实例id获取业务id
	 */
	public static String getBusnessKey(String processInstanceId) {
		ProcessInstance pi = getRuntimeService().createProcessInstanceQuery().processInstanceId(processInstanceId)
				.singleResult();
		if (pi != null) {
			return pi.getBusinessKey();
		}
		return null;
	}

	/**
	 * 中止流程(特权人直接审批通过等)
	 * 
	 * @param taskId
	 */
	public static void endProcess(String taskId, Map<String, Object> variables) throws Exception {

		TaskEntity task = findTaskById(taskId);
		if (null != task) {
			ProcessDefinition pd = ActivitiTool.getRepositoryService()
					.getProcessDefinition(task.getProcessDefinitionId());
			ActivityImpl endActivity = findActivitiImpl(taskId, "end");
			String lock=ActivitiTool.getProcessLock(pd.getKey());
			synchronized (lock) {
				try {
					commitProcess(taskId, variables, endActivity.getId());
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					lock.notifyAll();
				}
			}

		}

	}

	/**
	 * 根据任务ID和节点ID获取活动节点 <br>
	 * 
	 * @param taskId
	 *            任务ID
	 * @param activityId
	 *            活动节点ID <br>
	 *            如果为null或""，则默认查询当前活动节点 <br>
	 *            如果为"end"，则查询结束节点 <br>
	 * 
	 * @return
	 * @throws Exception
	 */
	private static ActivityImpl findActivitiImpl(String taskId, String activityId) throws Exception {
		// 取得流程定义
		ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);

		// 获取当前活动节点ID
		if (activityId == null || "".equals(activityId)) {
			activityId = findTaskById(taskId).getTaskDefinitionKey();
		}

		// 根据流程定义，获取该流程实例的结束节点
		if (activityId.toUpperCase().equals("END")) {
			for (ActivityImpl activityImpl : processDefinition.getActivities()) {
				List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
				if (pvmTransitionList.isEmpty()) {
					return activityImpl;
				}
			}
		}

		// 根据节点ID，获取对应的活动节点
		ActivityImpl activityImpl = ((ProcessDefinitionImpl) processDefinition).findActivity(activityId);

		return activityImpl;
	}

	/**
	 * @param taskId
	 *            当前任务ID
	 * @param variables
	 *            流程变量
	 * @param activityId
	 *            流程转向执行任务节点ID<br>
	 *            此参数为空，默认为提交操作
	 * @throws Exception
	 */
	private static void commitProcess(String taskId, Map<String, Object> variables, String activityId)
			throws Exception {
		if (variables == null) {
			variables = new HashMap<String, Object>();
		}
		// 跳转节点为空，默认提交操作
		if (activityId == null || "".equals(activityId)) {
			getTaskService().complete(taskId, variables);
		} else {// 流程转向操作
			turnTransition(taskId, activityId, variables);
		}
	}

	private static ProcessDefinitionEntity findProcessDefinitionEntityByTaskId(String taskId) throws Exception {
		// 取得流程定义
		ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) getRepositoryService())
				.getDeployedProcessDefinition(findTaskById(taskId).getProcessDefinitionId());

		if (processDefinition == null) {
			throw new Exception("流程定义未找到!");
		}

		return processDefinition;
	}

	/**
	 * 根据任务ID获得任务实例
	 * 
	 * @param taskId
	 *            任务ID
	 * @return
	 * @throws Exception
	 */
	private static TaskEntity findTaskById(String taskId) throws Exception {
		TaskEntity task = (TaskEntity) getTaskService().createTaskQuery().taskId(taskId).singleResult();
		if (task == null) {
			throw new Exception("任务实例未找到!");
		}
		return task;
	}

	private static void turnTransition(String taskId, String activityId, Map<String, Object> variables)
			throws Exception {
		// 当前节点
		ActivityImpl currActivity = findActivitiImpl(taskId, null);
		// 清空当前流向
		List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);

		// 创建新流向
		TransitionImpl newTransition = currActivity.createOutgoingTransition();
		// 目标节点
		ActivityImpl pointActivity = findActivitiImpl(taskId, activityId);
		// 设置新流向的目标节点
		newTransition.setDestination(pointActivity);

		// 执行转向任务
		getTaskService().complete(taskId, variables);
		// 删除目标节点新流入
		pointActivity.getIncomingTransitions().remove(newTransition);

		// 还原以前流向
		restoreTransition(currActivity, oriPvmTransitionList);
	}

	private static void restoreTransition(ActivityImpl activityImpl, List<PvmTransition> oriPvmTransitionList) {
		// 清空现有流向
		List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
		pvmTransitionList.clear();
		// 还原以前流向
		for (PvmTransition pvmTransition : oriPvmTransitionList) {
			pvmTransitionList.add(pvmTransition);
		}
	}

	private static List<PvmTransition> clearTransition(ActivityImpl activityImpl) {
		// 存储当前节点所有流向临时变量
		List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
		// 获取当前节点所有流向，存储到临时变量，然后清空
		List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
		for (PvmTransition pvmTransition : pvmTransitionList) {
			oriPvmTransitionList.add(pvmTransition);
		}
		pvmTransitionList.clear();

		return oriPvmTransitionList;
	}

	public static String getProcessLock(String processKey){
		String lock = processLockMap.get(processKey);
		if(StringUtil.isEmpty(lock)){
			lock = new String(processKey);
			processLockMap.put(processKey, lock);
		}
		return lock;
	}
	
	public static void main(String[] args) {
		SpringApplicationContext.setClassPathXmlApplicationContext("conf/conf-application.xml");
		getLastProcessDefinition();
		System.out.println(ActivitiTool.getAllTaskNode("test11"));
		System.out.println("end");
	}
}
